#ifndef _Ape_AutoPtr_h
#define _Ape_AutoPtr_h

#include <Ape/Object.h>
#include <Ape/UInt.h>
#include <Ape/ENullPointer.h>

namespace Ape {

	template<typename P_t>
	class AutoPtr : public Object {
		TYPE_NAME ("Ape::AutoPtr<P_t>")
	private:
		mutable P_t* m_Ptr;
	public:
		AutoPtr();
		AutoPtr (P_t* p);
		AutoPtr (const AutoPtr<P_t>& other);
		virtual ~AutoPtr();
		
		operator P_t* () const;
		P_t* operator -> () const;
		P_t& operator * () const;
		
		
		P_t* Release() const;
		
		virtual const String ToString() const;
	};
	
	// -------------------------------------- //
	
	template<typename P_t>
	AutoPtr<P_t>::AutoPtr()
			: m_Ptr (NULL)
	{}
	
	template<typename P_t>
	AutoPtr<P_t>::AutoPtr (P_t* p)
			: m_Ptr (p)
	{}
	
	template<typename P_t>
	AutoPtr<P_t>::AutoPtr (const AutoPtr<P_t>& other)
			: Object (other),
			m_Ptr (other.Release() )
	{}
	
	template<typename P_t>
	AutoPtr<P_t>::~AutoPtr() {
		del (m_Ptr);
	}
	
	template<typename P_t>
	AutoPtr<P_t>::operator P_t* () const {
		return m_Ptr;
	}
	
	template<typename P_t>
	P_t& AutoPtr<P_t>::operator * () const {
		if (!m_Ptr)
			throw ENullPointer (L"Attempt to derefference a NULL pointer");
		return *m_Ptr;
	}
	
	template<typename P_t>
	P_t* AutoPtr<P_t>::Release() const {
		P_t* p = m_Ptr;
		m_Ptr = NULL;
		return p;
	}
	template<typename P_t>
	P_t* AutoPtr<P_t>::operator -> () const {
		if (!m_Ptr)
			throw ENullPointer (L"Attempt to call a member of a NULL pointer");
		return m_Ptr;
	}
	
	template<typename P_t>
	const String AutoPtr<P_t>::ToString() const {
		return L"Ape::AutoPtr<P_t> " +
			   (m_Ptr ?
				(L"[0x" + UInt (uint (m_Ptr) ).ToString (radixHex) + L"] ")
				:
				String (L"[nil]")
			   );
	}
}

#endif // _Ape_AutoPtr_h
